/********************************************************************
*  sample.cu
*  This is a example of the CUDA program.
*********************************************************************/
#include <kulaCuda.h>

#define PI 3.14f

/************************************************************************/
/* Init CUDA                                                            */
/************************************************************************/
#if __DEVICE_EMULATION__

bool InitCUDASphere(void){return true;}

#else
bool InitCUDASphere(ofstream &out)
{
	int count = 0;
	int i = 0;

	cudaGetDeviceCount(&count);
	if(count == 0) {
		out<<"There is no device.\n";
		return false;
	}

	for(i = 0; i < count; i++) {
		cudaDeviceProp prop;
		if(cudaGetDeviceProperties(&prop, i) == cudaSuccess) {
			if(prop.major >= 1) {
				break;
			}
		}
	}
	if(i == count) {
		out<<"There is no device supporting CUDA.\n";
		return false;
	}
	cudaSetDevice(i);

	out<<"CUDA initialized.\n";
	return true;
}

#endif
/************************************************************************/
/* Example                                                              */
/************************************************************************/
__global__ void square_array(SimpleVertex *a, int N, float R)
{
	int idx = blockIdx.x * blockDim.x + threadIdx.x;
	int tX=idx;

	int X= tX%N;
	int Y= (int)((tX-X)/N);



	float stepX=-PI/2+X*(PI/(N-2));
	float stepY=Y*(2*PI/(N-2));

	//Calculate position
	a[tX].Pos.x=R*cos(stepX)*cos(stepY);
	a[tX].Pos.y=R*cos(stepX)*sin(stepY);
	a[tX].Pos.z=R*sin(stepX);

	//Calculate normal
	D3DXVECTOR3 v=a[tX].Pos;
	float l=sqrt(v.x*v.x+v.y*v.y+v.z*v.z);

	a[tX].Nor.x=v.x/l;
	a[tX].Nor.y=v.y/l;
	a[tX].Nor.z=v.z/l;



	a[tX].Tex.x=X*(1.0f/(N-1));
	a[tX].Tex.y=Y*(1.0f/(N-1));


}


/************************************************************************/
/* HelloCUDA                                                            */
/************************************************************************/
SimpleVertex* GenerateSphere(int s, float R, ofstream &out)
{

	if(!InitCUDASphere(out)) {
		return 0;
	}

	SimpleVertex *a_h, *a_d;  // Pointer to host & device arrays
	const int N = s*s;  // Number of elements in arrays
	size_t size = N * sizeof(SimpleVertex);
	
	a_h = (SimpleVertex *)malloc(size);        // Allocate array on host
	cudaMalloc((void **) &a_d, size);   // Allocate array on device
	
	// Initialize host array and copy it to CUDA device
	//for (int i=0; i<N; i++) a_h[i].Pos.x=(float)i*2; 
	cudaMemcpy(a_d, a_h, size, cudaMemcpyHostToDevice);
	
	// Do calculation on device:
	int block_size = 16;
	int n_blocks =N/block_size + (N%block_size == 0 ? 0:1);
	square_array <<< n_blocks, block_size >>> (a_d, s, R);
	
	// Retrieve result from device and store it in host array
	cudaMemcpy(a_h, a_d, sizeof(SimpleVertex)*N, cudaMemcpyDeviceToHost);
	out<<(cudaGetErrorString(cudaGetLastError()));
	// Print results
	for (int i=0; i<N; i++) out<< i<<" "<< a_h[i].Tex.x<<" "<< a_h[i].Tex.y<<endl;;
	
	// Cleanup
	cudaFree(a_d);

	return a_h;
}

